package com.majun.service.impl;


import com.majun.dao.OrderDao;
import com.majun.dao.UserDao;
import com.majun.entity.Order;
import com.majun.entity.User;
import com.majun.service.SimpleService;
import com.majun.utils.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Date;

/**
 * @author majun
 * @date 2025/04/16
 */
@Slf4j
@Service
// 不要这样使用事务，你领导不骂算我的骂的
// @Transactional
public class SimpleServiceImpl implements SimpleService {

    @Autowired
    private UserDao userDao;;

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 下面的 private 已经提示出错误，咱们就不用试一下了。肯定不支持事务。仔细看提示信息。
     * methods annotated with '@transactional' must be overridable
     */
    @Transactional(value = "", rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    private void add() {

    }

    /**
     * @Description: static、private修饰的方法。肯定不支持事务,仔细看提示信息。
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/11/1115:25
     */
    @Transactional
    public static void addStatic() {

    }


    /**
     * todo 其他事务失效的情况就不测试了
     * 1. 没有启用事务管理器<code>@EnableTransactionManagement</code>
     * 2. 没有开启事务注解<code>@Transactional</code>
     */


    /**
     * try -catch 事务失效
     */
    @Transactional
    @Override
    public void addTryCatch() {

        String txName = TransactionSynchronizationManager.getCurrentTransactionName();
        System.err.println("SimpleService.addTryCatch,txname=" + txName);

        //添加成功
        userDao.insert(getUser());
        try {
            orderDao.insert(getOrder());
            System.out.println(1 / 0);
        } catch (Exception e) {
            e.printStackTrace();
            // 抛出异常，才会被事务管理
             throw new RuntimeException("addTryCatch");
        }
    }

    /**
     * 捕获异常RuntimeException，但抛出了Exception。事务失效
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void addTryRuntimeException() throws Exception {
        try {
            userDao.insert(getUser());
            System.out.println(1 / 0);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("但抛出了Exception");
        }
    }

    /**
     * tomcat为主线程接受并处理请求，new therad 执行sql,导致事务失效
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addNewThread() {
        log.info("主线程：" + Thread.currentThread().getName());
        userDao.insert(getUser());
        new Thread(() -> {
            orderDao.insert(getOrder());
            // by zero 错误，但数据库没有回滚数据。
            int i = 1 / 0;
            System.out.println(i);
        }, "任务线程").start();
        // 此处抛出异常，order表回滚了，但任务线程提交了数据。
        System.out.println(1 / 0);
    }

    //-------------3，函数式事务，比较麻烦，一般不使用-------
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Override
//    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Transactional
    public void addNewThreadOK(){
        userDao.insert(getUser());

        String txName = TransactionSynchronizationManager.getCurrentTransactionName();
        System.out.println("addNewThreadOK,txname="+txName);
        transactionTemplate.execute(status->{
            String txName2 = TransactionSynchronizationManager.getCurrentTransactionName();
            System.out.println("addNewThreadOK,txname2="+txName2);
            orderDao.insert(getOrder());
            //status.flush();
            //用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。
            //status.setRollbackOnly();
            return Boolean.TRUE ;
        });
        System.out.println(1 / 0);
    }

    /**
     * 使用new关键字，导致事务没有被spring管理。从而导致事务失效
     */
    @Override
    public void addNew() {
        // 这种情况：新人会出现或者使用大量的设计模式，导致的没有发现（本人遇到过）
        SimpleService simpleService = new SimpleServiceImpl();
        simpleService.addAll();
    }


    /**
     * try -catch 事务失效
     */
    @Override
    @Transactional
    public void addAll() {
        UserDao userDao1 = SpringContextUtil.getBeansOfType(UserDao.class);
        OrderDao orderDao1 = SpringContextUtil.getBeansOfType(OrderDao.class);
        //添加成功
        userDao1.insert(getUser());
        orderDao1.insert(getOrder());
        System.out.println(1 / 0);
    }

    @Override
    @Transactional
    public void addPublicMethod() {
        String txName = TransactionSynchronizationManager.getCurrentTransactionName();
        System.err.println("addPublicMethod,txname=" + txName);
        userDao.insert(getUser());
        // 事务在这里生效
        this.addInnerMethod();
        System.out.println(1 / 0);
    }

//    @Override
    @Transactional
    public void addInnerMethod() {
        String txName = TransactionSynchronizationManager.getCurrentTransactionName();
        System.err.println("addInternalMethod,txname=" + txName);
        //添加成功
        orderDao.insert(getOrder());
//        System.out.println(1 / 0);
    }

    @Override
    public void addInnerMethodERROR() {
        String txName = TransactionSynchronizationManager.getCurrentTransactionName();
        System.err.println("addPublicMethod,txname=" + txName);
        userDao.insert(getUser());
        // 从 Spring 容器中获取代理对象
//        SimpleService proxy = applicationContext.getBean(SimpleService.class);
        // 通过代理对象调用方法，事务生效
        this.addInnerMethod();
        System.out.println(1 / 0);
    }

    @Override
    public void addInnerMethodOK() {
        // 从 Spring 容器中获取代理对象
        SimpleService proxy = applicationContext.getBean(SimpleService.class);
        // 通过代理对象调用方法，事务生效
//        proxy.addInnerMethod();
    }

//    @Override
//    public void addInnerMethodOK() {
////        // 获取代理对象
//        SimpleService proxy = (SimpleService) AopContext.currentProxy();
//        // 通过代理对象调用方法
//        proxy.addInnerMethod();
//    }


    /**
     * @return {@link User }
     */
    private User getUser() {
        User user = new User();
        user.setName("张三");
        user.setCreatedDate(new Date());
        return user;
    }


    /**
     * @return {@link Order }
     */
    private Order getOrder() {
        Order order = new Order();
        order.setNum("1010101");
        order.setCreatedDate(new Date());
        return order;
    }
}
